This notebook demonstrates samples in the PBTA cluster by experimental strategy and cancer type using dimensionality reduction techniques, namely, Principal Component Analysis (PCA), t-Distributed Stochastic Neighbor Embedding (t-SNE), and Uniform Manifold Approximation and Projection (UMAP).

It addresses issue #9 in the Open-PBTA analysis repository.

Output files:

  • analyses/unsupervised transcriptomic-analysis/plots/meta_grid_kallisto_method.pdf
  • analyses/unsupervised transcriptomic-analysis/plots/meta_grid_kallisto_type.pdf
  • analyses/unsupervised transcriptomic-analysis/plots/meta_grid_rsem_method.pdf
  • analyses/unsupervised transcriptomic-analysis/plots/meta_grid_rsem_type.pdf
  • analyses/unsupervised transcriptomic-analysis/plots/no_batch_grid_kallisto_method.pdf
  • analyses/unsupervised transcriptomic-analysis/plots/no_batch_grid_kallisto_type.pdf
  • analyses/unsupervised transcriptomic-analysis/plots/no_batch_grid_rsem_method.pdf
  • analyses/unsupervised transcriptomic-analysis/plots/no_batch_grid_rsem_method.pdf

Usage

This script is intended to be run via the command line from the top directory of the repository as follows:

Rscript -e "rmarkdown::render('analyses/unsupervised-transcriptomic-analysis/01-transcriptomic-analysis.Rmd', clean = TRUE)"

Set Up

Set up the functions and output directories. Read the metadata into a data.frame named df2. Read the expression data into data.frames named exp_kallisto and exp_rsem, respectively.

# This is needed to extract the legend of a ggplot
if (!("lemon" %in% installed.packages())) {
  install.packages("lemon")
}
# This is needed for batch effect correction 
if (!("limma" %in%installed.packages())) {
  install.packages("BiocManager")
  BiocManager::install("limma")
}
# This is needed for running the t-SNE analysis 
if (!("Rtsne" %in% installed.packages())) {
  install.packages("Rtsne")
}
# This is needed for running the umap analysis 
if (!("umap" %in% installed.packages())) {
  install.packages("umap")
}

# magrittr pipe
`%>%` <- dplyr::`%>%`

reduction_fn <- function(name, id) {
  # Given a data.frame that contains scores from a dimensionality reduction
  # technique, align the metadata and add the variables `type` and
  # `experimental_strategy` to the data.frame in preparation for plotting.
  #
  # Note: The rownames provided in the id argument are synonymous with the 
  # metadata's `Kids_First_Biospecimen_ID`
  #
  # Args:
  #   name: Name of the data.frame containing dimension reduction scores 
  #   id: Vector containing Biospecimen IDs extracted from the transposed expression data
  #
  # Returns:
  #   name: A data.frame containing the dimension reduction scores along with four 
  #         new variables as follows:
  #         ID: Values that correspond with `Kids_First_Biospecimen_ID`
  #         type: The cancer types that correspond with `disease_type_new`
  #         participant: Values that correspond with `Kids_First_Participant_ID`
  #         method: The experimental strategy used to sequence the data
  
  # Assign the relevant rownames(Kids_First_Biospecimen_ID) to column named `ID`
  name$ID <- id
  # Align the metadata with the `ID` variable in the data.frame
  metadata <- df2 %>%
    dplyr::filter(Kids_First_Biospecimen_ID %in% name$ID) %>%
    dplyr::filter(!duplicated(Kids_First_Biospecimen_ID))
  # Filter the data.frame to contain only the `ID` values common 
  # to the metadata
  name <- name %>%
    dplyr::filter(ID %in% metadata$Kids_First_Biospecimen_ID)
  # Assign cancer type to column `type` of the data.frame
  name$type <- as.factor(metadata$disease_type_new)
  # Assign Kids_First_Participant_ID to column `type` of the data.frame
  name$participant <- as.factor(metadata$Kids_First_Participant_ID)
  # Filter data.frame for unique participant ID
  name <- name %>%
    dplyr::filter(!duplicated(participant))
  # Filter for the participant ids found in `name` data.frame
  metadata <- df2 %>%
    dplyr::select(Kids_First_Biospecimen_ID, Kids_First_Participant_ID, experimental_strategy) %>%
    dplyr::filter(Kids_First_Participant_ID %in% name$participant) 
  # Filter the metadata for unique cases of `Kids_First_Participant_ID`
  metadata <- metadata %>%
    dplyr::filter(!duplicated(metadata$Kids_First_Participant_ID))
  # Assign the `experimental_strategy` variable from the metadata to `method` 
  # column in `name` data.frame
  name$method <- as.character(metadata$experimental_strategy) 
  # Rename "RNA-Seq" values in name$method to "Unknown" 
  # (we do not know the exact RNA sequencing method)
  name$method[name$method == "RNA-Seq"] <- "Unknown"
  # Reassign the values of name$method to as factors
  name$method <- as.factor(name$method) 
  
  return(name)
}

correct_batch_effects <- function(name) {
  # Given a data.frame that has been align with the metadata, 
  # correct for batch effects.
  #
  # Args:
  #   name: data.frame aligned with the metadata but not yet 
  #         corrected for batch effects
  #
  # Returns:
  #   name2: the now batch effect corrected data.frame 
  
  # Create a data.frame with just the dimension reduction scores and the 
  # experimental strategy
  name2 <- data.frame(name[, 1:2], method = name[, 6])
  
  # Treat the experimental strategies as characters
  name2$method <- as.character(name2$method)
  # Now replace each strategy with a unique batch number
  name2$method[name2$method == "Unknown"] <- "1"
  name2$method[name2$method == "WGS"] <- "2"
  name2$method[name2$method == "WXS"] <- "3"
  name2$method[name2$method == "Panel"] <- "4"
  # Now make these numbers factors 
  name2$method <- as.factor(name2$method)
  # Assign the batch numbers to a variable `batch`
  batch = name2$method
  # Correct batch effects
  name2 <- limma::removeBatchEffect(t(name2[, 1:2]), batch)
  # Tailor the new data.frame (free from batch effects) to include the 
  # additional variables we want to include, namely, the experimental strategy, 
  # the cancer type, the Kids_First_Biospecimen_ID, and the 
  # Kids_First_Participant_ID.
  name2 <- data.frame(t(name2))
  name2$method <- name$method
  name2$type <- name$type
  name2$ID <- name$ID
  name2$participant <- name$participant
  
  return(name2)
}

# Assign name of output directory
plots_dir <- "plots"

# Create directory to hold the output plots.
if (!dir.exists(plots_dir)) {
  dir.create(plots_dir)
}

# Read in dataset
df2 <- data.frame(readr::read_tsv(
  file.path("..", "..", "data", "pbta-histologies.tsv")
))
Parsed with column specification:
cols(
  .default = col_character(),
  age_at_diagnosis = col_double()
)
See spec(...) for full column specifications.
# Read in kallisto expression data 
exp_kallisto <- data.frame(readr::read_rds(
  file.path("..", "..", "data", "pbta-gene-expression-kallisto.rds")
))

# Read in RSEM expression data 
exp_rsem <- data.frame(readr::read_rds(
  file.path("..", "..", "data", "pbta-gene-expression-rsem.fpkm.rds")
))

Prep Data

Make the expression data.frame all numeric by transforming the non-numeric gene_id column into rownames. Transpose the data and save the rownames of the transposed data.frames for alignment to the metadata.

# Transform the non-numeric "gene_id" column into rownames 
exp_kallisto <- exp_kallisto[, -1] %>%
  dplyr::filter(!duplicated(gene_id)) %>%
  tibble::column_to_rownames("gene_id")

exp_rsem <- exp_rsem %>%
  tibble::column_to_rownames("gene_id") %>%
  na.omit()

# Transpose the data 
transposed_rsem_data <- t(exp_rsem)

transposed_kallisto_data <- t(exp_kallisto)

# Save rownames as a vector
rsem_ID <- rownames(transposed_rsem_data)
kallisto_ID <- rownames(transposed_kallisto_data)

RSEM

Run PCA

# Run PCA on RSEM 
rsem_pca <- prcomp(transposed_rsem_data)
# Make a data.frame with PCA scores
rsem_pca_data <- data.frame(rsem_pca$x[, 1:2])
# Run the reduction_fn which aligns metadata and prepares data.frame for ggplot
rsem_pca_data <- reduction_fn(rsem_pca_data, rsem_ID)
# Plot the PCA scores and color by cancer type 
rsem_pca_plot <- ggplot2::ggplot(rsem_pca_data,
                                 ggplot2::aes(x = rsem_pca_data[, 1],
                                              y = rsem_pca_data[, 2],
                                              color = type)) +
  ggplot2::geom_point() +
  ggplot2::theme(legend.position = "none")

# Plot the PCA scores and color by method
rsem_pca_plot_method <- ggplot2::ggplot(rsem_pca_data,
                                             ggplot2::aes(x = rsem_pca_data[, 1],
                                                          y = rsem_pca_data[, 2],
                                                          color = method)) +
  ggplot2::geom_point() +
  ggplot2::theme(legend.position = "none")

PCA Batch Effects Corrected

# Run the correct_batch_effects function which corrects for batch effects and prepares data.frame for ggplot
rsem_pca_data <- correct_batch_effects(rsem_pca_data)
# Plot the PCA scores and color by cancer type 
rsem_pca_plot_2 <- ggplot2::ggplot(rsem_pca_data,
                                 ggplot2::aes(x = rsem_pca_data[, 1],
                                              y = rsem_pca_data[, 2],
                                              color = type)) +
  ggplot2::geom_point() +
  ggplot2::theme(legend.position = "none")

# Plot the PCA scores and color by method
rsem_pca_plot_method_2 <- ggplot2::ggplot(rsem_pca_data,
                                             ggplot2::aes(x = rsem_pca_data[, 1],
                                                          y = rsem_pca_data[, 2],
                                                          color = method)) +
  ggplot2::geom_point() +
  ggplot2::theme(legend.position = "none")

Run t-SNE

# Run t-SNE on RSEM
rsem_tsne <- Rtsne::Rtsne(transposed_rsem_data)
# Make a data.frame with t-SNE scores
rsem_tsne_data <- data.frame(rsem_tsne$Y)
# Run the reduction_fn which aligns metadata and prepares data.frame for ggplot
rsem_tsne_data <- reduction_fn(rsem_tsne_data, rsem_ID)
# Plot the t-SNE scores and color by cancer type 
rsem_tsne_plot <- ggplot2::ggplot(rsem_tsne_data,
                                  ggplot2::aes(x = rsem_tsne_data[, 1],
                                               y = rsem_tsne_data[, 2],
                                               color = type)) +
  ggplot2::geom_point() +
  ggplot2::theme(legend.position = "none")

# Plot the t-SNE scores and color by method 
rsem_tsne_plot_method <- ggplot2::ggplot(rsem_tsne_data,
                                              ggplot2::aes(x = rsem_tsne_data[, 1],
                                                           y = rsem_tsne_data[, 2],
                                                           color = method)) +
  ggplot2::geom_point() +
  ggplot2::theme(legend.position = "none")

t-SNE Batch Effects Corrected

# Run the correct_batch_effects function which corrects for batch effects and prepares data.frame for ggplot
rsem_tsne_data <- correct_batch_effects(rsem_tsne_data)
# Plot the PCA scores and color by cancer type 
rsem_tsne_plot_2 <- ggplot2::ggplot(rsem_tsne_data,
                                 ggplot2::aes(x = rsem_tsne_data[, 1],
                                              y = rsem_tsne_data[, 2],
                                              color = type)) +
  ggplot2::geom_point() +
  ggplot2::theme(legend.position = "none")

# Plot the t-SNE scores and color by method
rsem_tsne_plot_method_2 <- ggplot2::ggplot(rsem_tsne_data,
                                             ggplot2::aes(x = rsem_tsne_data[, 1],
                                                          y = rsem_tsne_data[, 2],
                                                          color = method)) +
  ggplot2::geom_point() +
  ggplot2::theme(legend.position = "none")

Run UMAP

# Run UMAP on RSEM
rsem_umap <- umap::umap(transposed_rsem_data)
# Make a data.frame with umap scores
rsem_umap_data <- data.frame(rsem_umap$layout)
# Run the reduction_fn which aligns metadata and prepares data.frame for ggplot
rsem_umap_data <- reduction_fn(rsem_umap_data, rsem_ID)

# Plot the umap scores and color by cancer type 
rsem_umap_plot <- ggplot2::ggplot(rsem_umap_data,
                                  ggplot2::aes(x = rsem_umap_data[, 1],
                                               y = rsem_umap_data[, 2],
                                               color = type)) +
  ggplot2::geom_point() +
  ggplot2::theme(legend.position = "bottom", legend.text = ggplot2::element_text(size = 4))

# Extract the plot legend
legend <- lemon::g_legend(rsem_umap_plot)

# Plot the umap scores and color by method 
rsem_umap_plot_method <- ggplot2::ggplot(rsem_umap_data,
                                              ggplot2::aes(x = rsem_umap_data[, 1],
                                                           y = rsem_umap_data[, 2],
                                                           color = method)) +
  ggplot2::geom_point() +
  ggplot2::theme(legend.position = "bottom", legend.text = ggplot2::element_text(size = 14))

#Extract the plot legend
legend2 <- lemon::g_legend(rsem_umap_plot_method)

UMAP Batch Effects Corrected

# Run the correct_batch_effects function which corrects for batch effects and prepares data.frame for ggplot
rsem_umap_data <- correct_batch_effects(rsem_umap_data)
# Plot the PCA scores and color by cancer type 
rsem_umap_plot_2 <- ggplot2::ggplot(rsem_umap_data,
                                 ggplot2::aes(x = rsem_umap_data[, 1],
                                              y = rsem_umap_data[, 2],
                                              color = type)) +
  ggplot2::geom_point() +
  ggplot2::theme(legend.position = "bottom", legend.text = ggplot2::element_text(size = 4))

# Extract the plot legend
legend_batch <- lemon::g_legend(rsem_umap_plot_2)

# Plot the umap scores and color by method
rsem_umap_plot_method_2 <- ggplot2::ggplot(rsem_umap_data,
                                             ggplot2::aes(x = rsem_umap_data[, 1],
                                                          y = rsem_umap_data[, 2],
                                                          color = method)) +
  ggplot2::geom_point() +
  ggplot2::theme(legend.position = "bottom", legend.text = ggplot2::element_text(size = 14))

#Extract the plot legend
legend2_batch <- lemon::g_legend(rsem_umap_plot_method_2)

RSEM Plots Grid

The grid of plots below shows the dimension reductions scores for the RSEM data, colored by experimental sequencing strategy and tumor type, respectively.
The top set of plots represent the data before batch effects were corrected, while the bottom set of plots represent the data after batch effects were corrected. When comparing the plots, the presence of batch effects does not appear to be very significant.

# Plot grid with RSEM data colored by method vs tumor type
meta_grid_rsem_method <- gridExtra::grid.arrange(rsem_pca_plot_method,
                                     rsem_tsne_plot_method,
                                     rsem_umap_plot_method + 
                                       ggplot2::theme(legend.position = 'hidden'),
                                     nrow = 2,
                                     legend2,
                                     top = "Experimental Strategy (RSEM)")

meta_grid_rsem_method_2 <- gridExtra::grid.arrange(rsem_pca_plot_method_2,
                                     rsem_tsne_plot_method_2,
                                     rsem_umap_plot_method_2 + 
                                       ggplot2::theme(legend.position = 'hidden'),
                                     nrow = 2,
                                     legend2_batch,
                                     top = "Experimental Strategy - Batch Effects Corrected (RSEM)")

meta_grid_rsem_type <- gridExtra::grid.arrange(rsem_pca_plot,
                                     rsem_tsne_plot,
                                     rsem_umap_plot + 
                                       ggplot2::theme(legend.position = 'hidden'),
                                     nrow = 3,
                                     legend,
                                     top = "Expressed Tumor Types (RSEM)")

meta_grid_rsem_type_2 <- gridExtra::grid.arrange(rsem_pca_plot_2,
                                     rsem_tsne_plot_2,
                                     rsem_umap_plot_2 + 
                                       ggplot2::theme(legend.position = 'hidden'),
                                     nrow = 3,
                                     legend_batch,
                                     top = "Expressed Tumor Types - Batch Effects Corrected (RSEM)")

# Save grid
ggplot2::ggsave(file.path(plots_dir, "meta_grid_rsem_method.pdf"), meta_grid_rsem_method, width = 12, height = 18)
ggplot2::ggsave(file.path(plots_dir, "meta_grid_rsem_type.pdf"), meta_grid_rsem_type, width = 12, height = 18)
ggplot2::ggsave(file.path(plots_dir, "no_batch_grid_rsem_method.pdf"), meta_grid_rsem_method_2, width = 12, height = 18)
ggplot2::ggsave(file.path(plots_dir, "no_batch_grid_rsem_type.pdf"), meta_grid_rsem_type_2, width = 12, height = 18)

Kallisto

Run PCA

# Run PCA on kallisto
kallisto_pca <- prcomp(transposed_kallisto_data)
# Make a data.frame with PCA scores
kallisto_pca_data <- data.frame(kallisto_pca$x[, 1:2])
# Run the reduction_fn which aligns metadata and prepares data.frame for ggplot
kallisto_pca_data <- reduction_fn(kallisto_pca_data, kallisto_ID)
# Plot the PCA scores and color by cancer type 
kallisto_pca_plot <- ggplot2::ggplot(kallisto_pca_data,
                                     ggplot2::aes(x = kallisto_pca_data[, 1],
                                                  y = kallisto_pca_data[, 2],
                                                  color = type)) +
  ggplot2::geom_point() +
  ggplot2::theme(legend.position = "none")

# Plot the PCA scores and color by method
kallisto_pca_plot_method <- ggplot2::ggplot(kallisto_pca_data,
                                                 ggplot2::aes(x = kallisto_pca_data[, 1],
                                                              y = kallisto_pca_data[, 2],
                                                              color = method)) +
  ggplot2::geom_point() +
  ggplot2::theme(legend.position = "none")

PCA Batch Effects Corrected

# Run the correct_batch_effects function which corrects for batch effects and prepares data.frame for ggplot
kallisto_pca_data <- correct_batch_effects(kallisto_pca_data)
# Plot the PCA scores and color by cancer type 
kallisto_pca_plot_2 <- ggplot2::ggplot(kallisto_pca_data,
                                 ggplot2::aes(x = kallisto_pca_data[, 1],
                                              y = kallisto_pca_data[, 2],
                                              color = type)) +
  ggplot2::geom_point() +
  ggplot2::theme(legend.position = "none")

# Plot the PCA scores and color by method
kallisto_pca_plot_method_2 <- ggplot2::ggplot(kallisto_pca_data,
                                             ggplot2::aes(x = kallisto_pca_data[, 1],
                                                          y = kallisto_pca_data[, 2],
                                                          color = method)) +
  ggplot2::geom_point() +
  ggplot2::theme(legend.position = "none")

Run t-SNE

# Run t-SNE on kallisto
kallisto_tsne <- Rtsne::Rtsne(transposed_kallisto_data)
# Make a data.frame with t-SNE scores
kallisto_tsne_data <- data.frame(kallisto_tsne$Y)
# Run the reduction_fn which aligns metadata and prepares data.frame for ggplot
kallisto_tsne_data <- reduction_fn(kallisto_tsne_data, kallisto_ID)
# Plot the t-SNE scores and color by cancer type 
kallisto_tsne_plot <- ggplot2::ggplot(kallisto_tsne_data,
                                      ggplot2::aes(x = kallisto_tsne_data[, 1],
                                                   y = kallisto_tsne_data[, 2],
                                                   color = type)) +
  ggplot2::geom_point() +
  ggplot2::theme(legend.position = "none")

# Plot the t-SNE scores and color by method 
kallisto_tsne_plot_method <- ggplot2::ggplot(kallisto_tsne_data,
                                                  ggplot2::aes(x = kallisto_tsne_data[, 1],
                                                               y = kallisto_tsne_data[, 2],
                                                               color = method)) +
  ggplot2::geom_point() +
  ggplot2::theme(legend.position = "none")

t-SNE Batch Effects Corrected

# Run the correct_batch_effects function which corrects for batch effects and prepares data.frame for ggplot
kallisto_tsne_data <- correct_batch_effects(kallisto_tsne_data)
# Plot the PCA scores and color by cancer type 
kallisto_tsne_plot_2 <- ggplot2::ggplot(kallisto_tsne_data,
                                 ggplot2::aes(x = kallisto_tsne_data[, 1],
                                              y = kallisto_tsne_data[, 2],
                                              color = type)) +
  ggplot2::geom_point() +
  ggplot2::theme(legend.position = "none")

# Plot the t-SNE scores and color by method
kallisto_tsne_plot_method_2 <- ggplot2::ggplot(kallisto_tsne_data,
                                             ggplot2::aes(x = kallisto_tsne_data[, 1],
                                                          y = kallisto_tsne_data[, 2],
                                                          color = method)) +
  ggplot2::geom_point() +
  ggplot2::theme(legend.position = "none")

Run UMAP

# Run UMAP on kallisto
kallisto_umap <- umap::umap(transposed_kallisto_data)
# Make a data.frame with umap scores
kallisto_umap_data <- data.frame(kallisto_umap$layout)
# Run the reduction_fn which aligns metadata and prepares data.frame for ggplot
kallisto_umap_data <- reduction_fn(kallisto_umap_data, kallisto_ID)
# Plot the umap scores and color by cancer type 
kallisto_umap_plot <- ggplot2::ggplot(kallisto_umap_data,
                                      ggplot2::aes(x = kallisto_umap_data[, 1],
                                                   y = kallisto_umap_data[, 2],
                                                   color = type)) +
  ggplot2::geom_point() +
  ggplot2::theme(legend.position = "bottom", legend.text = ggplot2::element_text(size = 4))

#Extract the plot legend
legend <- lemon::g_legend(kallisto_umap_plot)

# Plot the umap scores and color by method
kallisto_umap_plot_method <- ggplot2::ggplot(kallisto_umap_data,
                                                  ggplot2::aes(x = kallisto_umap_data[, 1],
                                                               y = kallisto_umap_data[, 2],
                                                               color = method)) +
  ggplot2::geom_point() +
  ggplot2::theme(legend.position = "bottom", legend.text = ggplot2::element_text(size = 14))

# Extract the plot legend 
legend2 <- lemon::g_legend(kallisto_umap_plot_method)

UMAP Batch Effects Corrected

# Run the correct_batch_effects function which corrects for batch effects and prepares data.frame for ggplot
kallisto_umap_data <- correct_batch_effects(kallisto_umap_data)
# Plot the PCA scores and color by cancer type 
kallisto_umap_plot_2 <- ggplot2::ggplot(kallisto_umap_data,
                                 ggplot2::aes(x = kallisto_umap_data[, 1],
                                              y = kallisto_umap_data[, 2],
                                              color = type)) +
  ggplot2::geom_point() +
  ggplot2::theme(legend.position = "bottom", legend.text = ggplot2::element_text(size = 4))

# Extract the plot legend
legend_batch <- lemon::g_legend(kallisto_umap_plot_2)

# Plot the umap scores and color by method
kallisto_umap_plot_method_2 <- ggplot2::ggplot(kallisto_umap_data,
                                             ggplot2::aes(x = kallisto_umap_data[, 1],
                                                          y = kallisto_umap_data[, 2],
                                                          color = method)) +
  ggplot2::geom_point() +
  ggplot2::theme(legend.position = "bottom", legend.text = ggplot2::element_text(size = 14))

#Extract the plot legend
legend2_batch <- lemon::g_legend(kallisto_umap_plot_method_2)

Kallisto Plots Grid

The grid of plots below shows the dimension reductions scores for the kallisto data, colored by experimental sequencing strategy and tumor type, respectively. The top set of plots represent the data before batch effects were corrected, while the bottom set of plots represent the data after batch effects were corrected. When comparing the plots, the presence of batch effects does not appear to be very significant.

# Plot grid with kallisto data colored by method vs tumor type
meta_grid_kallisto_method <- gridExtra::grid.arrange(kallisto_pca_plot_method,
                                              kallisto_tsne_plot_method,
                                              kallisto_umap_plot_method + 
                                                ggplot2::theme(legend.position = 'hidden'),
                                              legend2, 
                                              nrow = 2,
                                              top = "Experimental Strategy (kallisto)")

meta_grid_kallisto_method_2 <- gridExtra::grid.arrange(kallisto_pca_plot_method_2,
                                     kallisto_tsne_plot_method_2,
                                     kallisto_umap_plot_method_2 + 
                                       ggplot2::theme(legend.position = 'hidden'),
                                     nrow = 2,
                                     legend2_batch,
                                     top = "Experimental Strategy - Batch Effects Corrected (kallisto)")

meta_grid_kallisto_type <- gridExtra::grid.arrange(kallisto_pca_plot,
                                              kallisto_tsne_plot,
                                              kallisto_umap_plot + 
                                                ggplot2::theme(legend.position = 'hidden'),
                                              legend,
                                              nrow = 3,
                                              top = "Expressed Tumor Types (kallisto)")

meta_grid_kallisto_type_2 <- gridExtra::grid.arrange(kallisto_pca_plot_2,
                                     kallisto_tsne_plot_2,
                                     kallisto_umap_plot_2 + 
                                       ggplot2::theme(legend.position = 'hidden'),
                                     nrow = 3,
                                     legend_batch,
                                     top = "Expressed Tumor Types - Batch Effects Corrected (kallisto)")

# Save grid
ggplot2::ggsave(file.path(plots_dir, "meta_grid_kallisto_method.pdf"), meta_grid_kallisto_method, width = 12, height = 19)
ggplot2::ggsave(file.path(plots_dir, "meta_grid_kallisto_type.pdf"), meta_grid_kallisto_type, width = 12, height = 19)
ggplot2::ggsave(file.path(plots_dir, "no_batch_grid_kallisto_method.pdf"), meta_grid_kallisto_method_2, width = 12, height = 18)
ggplot2::ggsave(file.path(plots_dir, "no_batch_grid_kallisto_type.pdf"), meta_grid_kallisto_type_2, width = 12, height = 18)

Session Info

sessionInfo()
R version 3.6.1 (2019-07-05)
Platform: x86_64-apple-darwin15.6.0 (64-bit)
Running under: macOS Mojave 10.14.4

Matrix products: default
BLAS:   /Library/Frameworks/R.framework/Versions/3.6/Resources/lib/libRblas.0.dylib
LAPACK: /Library/Frameworks/R.framework/Versions/3.6/Resources/lib/libRlapack.dylib

locale:
[1] en_US.UTF-8/en_US.UTF-8/en_US.UTF-8/C/en_US.UTF-8/en_US.UTF-8

attached base packages:
[1] stats     graphics  grDevices utils     datasets  methods   base     

loaded via a namespace (and not attached):
 [1] Rcpp_1.0.2       umap_0.2.3       RSpectra_0.15-0  plyr_1.8.4      
 [5] pillar_1.4.2     compiler_3.6.1   base64enc_0.1-3  tools_3.6.1     
 [9] zeallot_0.1.0    digest_0.6.20    lattice_0.20-38  jsonlite_1.6    
[13] evaluate_0.14    Rtsne_0.15       tibble_2.1.3     gtable_0.3.0    
[17] pkgconfig_2.0.2  rlang_0.4.0      Matrix_1.2-17    yaml_2.2.0      
[21] lemon_0.4.3      xfun_0.8         gridExtra_2.3    dplyr_0.8.3     
[25] stringr_1.4.0    knitr_1.24       vctrs_0.2.0      askpass_1.1     
[29] hms_0.5.0        grid_3.6.1       tidyselect_0.2.5 reticulate_1.13 
[33] glue_1.3.1       R6_2.4.0         rmarkdown_1.14   limma_3.41.15   
[37] readr_1.3.1      purrr_0.3.2      ggplot2_3.2.1    magrittr_1.5    
[41] backports_1.1.4  scales_1.0.0     htmltools_0.3.6  assertthat_0.2.1
[45] colorspace_1.4-1 labeling_0.3     stringi_1.4.3    lazyeval_0.2.2  
[49] openssl_1.4.1    munsell_0.5.0    crayon_1.3.4    
LS0tCnRpdGxlOiAiVW5zdXBlcnZpc2VkIEFuYWx5c2lzIG9mIFRyYW5zY3JpcHRvbWljIERpZmZlcmVuY2VzIgphdXRob3I6IENoYW50ZSBCZXRoZWxsIGZvciBDQ0RMIDIwMTkKb3V0cHV0OgogIGh0bWxfbm90ZWJvb2s6CiAgICB0b2M6IHRydWUKICAgIHRvY19mbG9hdDogdHJ1ZQotLS0KClRoaXMgbm90ZWJvb2sgZGVtb25zdHJhdGVzIHNhbXBsZXMgaW4gdGhlIFBCVEEgY2x1c3RlciBieSBleHBlcmltZW50YWwgc3RyYXRlZ3kgYW5kIGNhbmNlciB0eXBlIHVzaW5nIGRpbWVuc2lvbmFsaXR5IHJlZHVjdGlvbiB0ZWNobmlxdWVzLCBuYW1lbHksIFtQcmluY2lwYWwgQ29tcG9uZW50IEFuYWx5c2lzXShodHRwczovL3d3dy5yZG9jdW1lbnRhdGlvbi5vcmcvcGFja2FnZXMvc3RhdHMvdmVyc2lvbnMvMy42LjAvdG9waWNzL3ByY29tcCkgKFBDQSksIFt0LURpc3RyaWJ1dGVkIFN0b2NoYXN0aWMgTmVpZ2hib3IgRW1iZWRkaW5nXShodHRwczovL2NyYW4uci1wcm9qZWN0Lm9yZy93ZWIvcGFja2FnZXMvUnRzbmUvUnRzbmUucGRmKSAodC1TTkUpLCBhbmQgW1VuaWZvcm0gTWFuaWZvbGQgQXBwcm94aW1hdGlvbiBhbmQgUHJvamVjdGlvbl0oaHR0cHM6Ly9jcmFuLnItcHJvamVjdC5vcmcvd2ViL3BhY2thZ2VzL3VtYXAvdmlnbmV0dGVzL3VtYXAuaHRtbCkgKFVNQVApLgoKSXQgYWRkcmVzc2VzIFtpc3N1ZSAjOV0oaHR0cHM6Ly9naXRodWIuY29tL0FsZXhzTGVtb25hZGUvT3BlblBCVEEtYW5hbHlzaXMvaXNzdWVzLzkpIGluIHRoZSBPcGVuLVBCVEEgYW5hbHlzaXMgcmVwb3NpdG9yeS4gCgojIyBPdXRwdXQgZmlsZXM6Ci0gYGFuYWx5c2VzL3Vuc3VwZXJ2aXNlZCB0cmFuc2NyaXB0b21pYy1hbmFseXNpcy9wbG90cy9tZXRhX2dyaWRfa2FsbGlzdG9fbWV0aG9kLnBkZmAKLSBgYW5hbHlzZXMvdW5zdXBlcnZpc2VkIHRyYW5zY3JpcHRvbWljLWFuYWx5c2lzL3Bsb3RzL21ldGFfZ3JpZF9rYWxsaXN0b190eXBlLnBkZmAKLSBgYW5hbHlzZXMvdW5zdXBlcnZpc2VkIHRyYW5zY3JpcHRvbWljLWFuYWx5c2lzL3Bsb3RzL21ldGFfZ3JpZF9yc2VtX21ldGhvZC5wZGZgCi0gYGFuYWx5c2VzL3Vuc3VwZXJ2aXNlZCB0cmFuc2NyaXB0b21pYy1hbmFseXNpcy9wbG90cy9tZXRhX2dyaWRfcnNlbV90eXBlLnBkZmAKLSBgYW5hbHlzZXMvdW5zdXBlcnZpc2VkIHRyYW5zY3JpcHRvbWljLWFuYWx5c2lzL3Bsb3RzL25vX2JhdGNoX2dyaWRfa2FsbGlzdG9fbWV0aG9kLnBkZmAKLSBgYW5hbHlzZXMvdW5zdXBlcnZpc2VkIHRyYW5zY3JpcHRvbWljLWFuYWx5c2lzL3Bsb3RzL25vX2JhdGNoX2dyaWRfa2FsbGlzdG9fdHlwZS5wZGZgCi0gYGFuYWx5c2VzL3Vuc3VwZXJ2aXNlZCB0cmFuc2NyaXB0b21pYy1hbmFseXNpcy9wbG90cy9ub19iYXRjaF9ncmlkX3JzZW1fbWV0aG9kLnBkZmAKLSBgYW5hbHlzZXMvdW5zdXBlcnZpc2VkIHRyYW5zY3JpcHRvbWljLWFuYWx5c2lzL3Bsb3RzL25vX2JhdGNoX2dyaWRfcnNlbV9tZXRob2QucGRmYAoKIyBVc2FnZQoKVGhpcyBzY3JpcHQgaXMgaW50ZW5kZWQgdG8gYmUgcnVuIHZpYSB0aGUgY29tbWFuZCBsaW5lIGZyb20gdGhlIHRvcCBkaXJlY3Rvcnkgb2YgdGhlIHJlcG9zaXRvcnkgYXMgZm9sbG93czogCmBgYApSc2NyaXB0IC1lICJybWFya2Rvd246OnJlbmRlcignYW5hbHlzZXMvdW5zdXBlcnZpc2VkLXRyYW5zY3JpcHRvbWljLWFuYWx5c2lzLzAxLXRyYW5zY3JpcHRvbWljLWFuYWx5c2lzLlJtZCcsIGNsZWFuID0gVFJVRSkiCgpgYGAKCiMgU2V0IFVwClNldCB1cCB0aGUgZnVuY3Rpb25zIGFuZCBvdXRwdXQgZGlyZWN0b3JpZXMuClJlYWQgdGhlIG1ldGFkYXRhIGludG8gYSBkYXRhLmZyYW1lIG5hbWVkIGBkZjJgLgpSZWFkIHRoZSBleHByZXNzaW9uIGRhdGEgaW50byBkYXRhLmZyYW1lcyBuYW1lZCBgZXhwX2thbGxpc3RvYCBhbmQgYGV4cF9yc2VtYCwgcmVzcGVjdGl2ZWx5LgoKYGBge3J9CgojIFRoaXMgaXMgbmVlZGVkIHRvIGV4dHJhY3QgdGhlIGxlZ2VuZCBvZiBhIGdncGxvdAppZiAoISgibGVtb24iICVpbiUgaW5zdGFsbGVkLnBhY2thZ2VzKCkpKSB7CiAgaW5zdGFsbC5wYWNrYWdlcygibGVtb24iKQp9CiMgVGhpcyBpcyBuZWVkZWQgZm9yIGJhdGNoIGVmZmVjdCBjb3JyZWN0aW9uIAppZiAoISgibGltbWEiICVpbiVpbnN0YWxsZWQucGFja2FnZXMoKSkpIHsKICBpbnN0YWxsLnBhY2thZ2VzKCJCaW9jTWFuYWdlciIpCiAgQmlvY01hbmFnZXI6Omluc3RhbGwoImxpbW1hIikKfQojIFRoaXMgaXMgbmVlZGVkIGZvciBydW5uaW5nIHRoZSB0LVNORSBhbmFseXNpcyAKaWYgKCEoIlJ0c25lIiAlaW4lIGluc3RhbGxlZC5wYWNrYWdlcygpKSkgewogIGluc3RhbGwucGFja2FnZXMoIlJ0c25lIikKfQojIFRoaXMgaXMgbmVlZGVkIGZvciBydW5uaW5nIHRoZSB1bWFwIGFuYWx5c2lzIAppZiAoISgidW1hcCIgJWluJSBpbnN0YWxsZWQucGFja2FnZXMoKSkpIHsKICBpbnN0YWxsLnBhY2thZ2VzKCJ1bWFwIikKfQoKIyBtYWdyaXR0ciBwaXBlCmAlPiVgIDwtIGRwbHlyOjpgJT4lYAoKcmVkdWN0aW9uX2ZuIDwtIGZ1bmN0aW9uKG5hbWUsIGlkKSB7CiAgIyBHaXZlbiBhIGRhdGEuZnJhbWUgdGhhdCBjb250YWlucyBzY29yZXMgZnJvbSBhIGRpbWVuc2lvbmFsaXR5IHJlZHVjdGlvbgogICMgdGVjaG5pcXVlLCBhbGlnbiB0aGUgbWV0YWRhdGEgYW5kIGFkZCB0aGUgdmFyaWFibGVzIGB0eXBlYCBhbmQKICAjIGBleHBlcmltZW50YWxfc3RyYXRlZ3lgIHRvIHRoZSBkYXRhLmZyYW1lIGluIHByZXBhcmF0aW9uIGZvciBwbG90dGluZy4KICAjCiAgIyBOb3RlOiBUaGUgcm93bmFtZXMgcHJvdmlkZWQgaW4gdGhlIGlkIGFyZ3VtZW50IGFyZSBzeW5vbnltb3VzIHdpdGggdGhlIAogICMgbWV0YWRhdGEncyBgS2lkc19GaXJzdF9CaW9zcGVjaW1lbl9JRGAKICAjCiAgIyBBcmdzOgogICMgICBuYW1lOiBOYW1lIG9mIHRoZSBkYXRhLmZyYW1lIGNvbnRhaW5pbmcgZGltZW5zaW9uIHJlZHVjdGlvbiBzY29yZXMgCiAgIyAgIGlkOiBWZWN0b3IgY29udGFpbmluZyBCaW9zcGVjaW1lbiBJRHMgZXh0cmFjdGVkIGZyb20gdGhlIHRyYW5zcG9zZWQgZXhwcmVzc2lvbiBkYXRhCiAgIwogICMgUmV0dXJuczoKICAjICAgbmFtZTogQSBkYXRhLmZyYW1lIGNvbnRhaW5pbmcgdGhlIGRpbWVuc2lvbiByZWR1Y3Rpb24gc2NvcmVzIGFsb25nIHdpdGggZm91ciAKICAjICAgICAgICAgbmV3IHZhcmlhYmxlcyBhcyBmb2xsb3dzOgogICMgICAgICAgICBJRDogVmFsdWVzIHRoYXQgY29ycmVzcG9uZCB3aXRoIGBLaWRzX0ZpcnN0X0Jpb3NwZWNpbWVuX0lEYAogICMgICAgICAgICB0eXBlOiBUaGUgY2FuY2VyIHR5cGVzIHRoYXQgY29ycmVzcG9uZCB3aXRoIGBkaXNlYXNlX3R5cGVfbmV3YAogICMgICAgICAgICBwYXJ0aWNpcGFudDogVmFsdWVzIHRoYXQgY29ycmVzcG9uZCB3aXRoIGBLaWRzX0ZpcnN0X1BhcnRpY2lwYW50X0lEYAogICMgICAgICAgICBtZXRob2Q6IFRoZSBleHBlcmltZW50YWwgc3RyYXRlZ3kgdXNlZCB0byBzZXF1ZW5jZSB0aGUgZGF0YQogIAogICMgQXNzaWduIHRoZSByZWxldmFudCByb3duYW1lcyhLaWRzX0ZpcnN0X0Jpb3NwZWNpbWVuX0lEKSB0byBjb2x1bW4gbmFtZWQgYElEYAogIG5hbWUkSUQgPC0gaWQKICAjIEFsaWduIHRoZSBtZXRhZGF0YSB3aXRoIHRoZSBgSURgIHZhcmlhYmxlIGluIHRoZSBkYXRhLmZyYW1lCiAgbWV0YWRhdGEgPC0gZGYyICU+JQogICAgZHBseXI6OmZpbHRlcihLaWRzX0ZpcnN0X0Jpb3NwZWNpbWVuX0lEICVpbiUgbmFtZSRJRCkgJT4lCiAgICBkcGx5cjo6ZmlsdGVyKCFkdXBsaWNhdGVkKEtpZHNfRmlyc3RfQmlvc3BlY2ltZW5fSUQpKQogICMgRmlsdGVyIHRoZSBkYXRhLmZyYW1lIHRvIGNvbnRhaW4gb25seSB0aGUgYElEYCB2YWx1ZXMgY29tbW9uIAogICMgdG8gdGhlIG1ldGFkYXRhCiAgbmFtZSA8LSBuYW1lICU+JQogICAgZHBseXI6OmZpbHRlcihJRCAlaW4lIG1ldGFkYXRhJEtpZHNfRmlyc3RfQmlvc3BlY2ltZW5fSUQpCiAgIyBBc3NpZ24gY2FuY2VyIHR5cGUgdG8gY29sdW1uIGB0eXBlYCBvZiB0aGUgZGF0YS5mcmFtZQogIG5hbWUkdHlwZSA8LSBhcy5mYWN0b3IobWV0YWRhdGEkZGlzZWFzZV90eXBlX25ldykKICAjIEFzc2lnbiBLaWRzX0ZpcnN0X1BhcnRpY2lwYW50X0lEIHRvIGNvbHVtbiBgdHlwZWAgb2YgdGhlIGRhdGEuZnJhbWUKICBuYW1lJHBhcnRpY2lwYW50IDwtIGFzLmZhY3RvcihtZXRhZGF0YSRLaWRzX0ZpcnN0X1BhcnRpY2lwYW50X0lEKQogICMgRmlsdGVyIGRhdGEuZnJhbWUgZm9yIHVuaXF1ZSBwYXJ0aWNpcGFudCBJRAogIG5hbWUgPC0gbmFtZSAlPiUKICAgIGRwbHlyOjpmaWx0ZXIoIWR1cGxpY2F0ZWQocGFydGljaXBhbnQpKQogICMgRmlsdGVyIGZvciB0aGUgcGFydGljaXBhbnQgaWRzIGZvdW5kIGluIGBuYW1lYCBkYXRhLmZyYW1lCiAgbWV0YWRhdGEgPC0gZGYyICU+JQogICAgZHBseXI6OnNlbGVjdChLaWRzX0ZpcnN0X0Jpb3NwZWNpbWVuX0lELCBLaWRzX0ZpcnN0X1BhcnRpY2lwYW50X0lELCBleHBlcmltZW50YWxfc3RyYXRlZ3kpICU+JQogICAgZHBseXI6OmZpbHRlcihLaWRzX0ZpcnN0X1BhcnRpY2lwYW50X0lEICVpbiUgbmFtZSRwYXJ0aWNpcGFudCkgCiAgIyBGaWx0ZXIgdGhlIG1ldGFkYXRhIGZvciB1bmlxdWUgY2FzZXMgb2YgYEtpZHNfRmlyc3RfUGFydGljaXBhbnRfSURgCiAgbWV0YWRhdGEgPC0gbWV0YWRhdGEgJT4lCiAgICBkcGx5cjo6ZmlsdGVyKCFkdXBsaWNhdGVkKG1ldGFkYXRhJEtpZHNfRmlyc3RfUGFydGljaXBhbnRfSUQpKQogICMgQXNzaWduIHRoZSBgZXhwZXJpbWVudGFsX3N0cmF0ZWd5YCB2YXJpYWJsZSBmcm9tIHRoZSBtZXRhZGF0YSB0byBgbWV0aG9kYCAKICAjIGNvbHVtbiBpbiBgbmFtZWAgZGF0YS5mcmFtZQogIG5hbWUkbWV0aG9kIDwtIGFzLmNoYXJhY3RlcihtZXRhZGF0YSRleHBlcmltZW50YWxfc3RyYXRlZ3kpIAogICMgUmVuYW1lICJSTkEtU2VxIiB2YWx1ZXMgaW4gbmFtZSRtZXRob2QgdG8gIlVua25vd24iIAogICMgKHdlIGRvIG5vdCBrbm93IHRoZSBleGFjdCBSTkEgc2VxdWVuY2luZyBtZXRob2QpCiAgbmFtZSRtZXRob2RbbmFtZSRtZXRob2QgPT0gIlJOQS1TZXEiXSA8LSAiVW5rbm93biIKICAjIFJlYXNzaWduIHRoZSB2YWx1ZXMgb2YgbmFtZSRtZXRob2QgdG8gYXMgZmFjdG9ycwogIG5hbWUkbWV0aG9kIDwtIGFzLmZhY3RvcihuYW1lJG1ldGhvZCkgCiAgCiAgcmV0dXJuKG5hbWUpCn0KCmNvcnJlY3RfYmF0Y2hfZWZmZWN0cyA8LSBmdW5jdGlvbihuYW1lKSB7CiAgIyBHaXZlbiBhIGRhdGEuZnJhbWUgdGhhdCBoYXMgYmVlbiBhbGlnbiB3aXRoIHRoZSBtZXRhZGF0YSwgCiAgIyBjb3JyZWN0IGZvciBiYXRjaCBlZmZlY3RzLgogICMKICAjIEFyZ3M6CiAgIyAgIG5hbWU6IGRhdGEuZnJhbWUgYWxpZ25lZCB3aXRoIHRoZSBtZXRhZGF0YSBidXQgbm90IHlldCAKICAjICAgICAgICAgY29ycmVjdGVkIGZvciBiYXRjaCBlZmZlY3RzCiAgIwogICMgUmV0dXJuczoKICAjICAgbmFtZTI6IHRoZSBub3cgYmF0Y2ggZWZmZWN0IGNvcnJlY3RlZCBkYXRhLmZyYW1lIAogIAogICMgQ3JlYXRlIGEgZGF0YS5mcmFtZSB3aXRoIGp1c3QgdGhlIGRpbWVuc2lvbiByZWR1Y3Rpb24gc2NvcmVzIGFuZCB0aGUgCiAgIyBleHBlcmltZW50YWwgc3RyYXRlZ3kKICBuYW1lMiA8LSBkYXRhLmZyYW1lKG5hbWVbLCAxOjJdLCBtZXRob2QgPSBuYW1lWywgNl0pCiAgCiAgIyBUcmVhdCB0aGUgZXhwZXJpbWVudGFsIHN0cmF0ZWdpZXMgYXMgY2hhcmFjdGVycwogIG5hbWUyJG1ldGhvZCA8LSBhcy5jaGFyYWN0ZXIobmFtZTIkbWV0aG9kKQogICMgTm93IHJlcGxhY2UgZWFjaCBzdHJhdGVneSB3aXRoIGEgdW5pcXVlIGJhdGNoIG51bWJlcgogIG5hbWUyJG1ldGhvZFtuYW1lMiRtZXRob2QgPT0gIlVua25vd24iXSA8LSAiMSIKICBuYW1lMiRtZXRob2RbbmFtZTIkbWV0aG9kID09ICJXR1MiXSA8LSAiMiIKICBuYW1lMiRtZXRob2RbbmFtZTIkbWV0aG9kID09ICJXWFMiXSA8LSAiMyIKICBuYW1lMiRtZXRob2RbbmFtZTIkbWV0aG9kID09ICJQYW5lbCJdIDwtICI0IgogICMgTm93IG1ha2UgdGhlc2UgbnVtYmVycyBmYWN0b3JzIAogIG5hbWUyJG1ldGhvZCA8LSBhcy5mYWN0b3IobmFtZTIkbWV0aG9kKQogICMgQXNzaWduIHRoZSBiYXRjaCBudW1iZXJzIHRvIGEgdmFyaWFibGUgYGJhdGNoYAogIGJhdGNoID0gbmFtZTIkbWV0aG9kCiAgIyBDb3JyZWN0IGJhdGNoIGVmZmVjdHMKICBuYW1lMiA8LSBsaW1tYTo6cmVtb3ZlQmF0Y2hFZmZlY3QodChuYW1lMlssIDE6Ml0pLCBiYXRjaCkKICAjIFRhaWxvciB0aGUgbmV3IGRhdGEuZnJhbWUgKGZyZWUgZnJvbSBiYXRjaCBlZmZlY3RzKSB0byBpbmNsdWRlIHRoZSAKICAjIGFkZGl0aW9uYWwgdmFyaWFibGVzIHdlIHdhbnQgdG8gaW5jbHVkZSwgbmFtZWx5LCB0aGUgZXhwZXJpbWVudGFsIHN0cmF0ZWd5LCAKICAjIHRoZSBjYW5jZXIgdHlwZSwgdGhlIEtpZHNfRmlyc3RfQmlvc3BlY2ltZW5fSUQsIGFuZCB0aGUgCiAgIyBLaWRzX0ZpcnN0X1BhcnRpY2lwYW50X0lELgogIG5hbWUyIDwtIGRhdGEuZnJhbWUodChuYW1lMikpCiAgbmFtZTIkbWV0aG9kIDwtIG5hbWUkbWV0aG9kCiAgbmFtZTIkdHlwZSA8LSBuYW1lJHR5cGUKICBuYW1lMiRJRCA8LSBuYW1lJElECiAgbmFtZTIkcGFydGljaXBhbnQgPC0gbmFtZSRwYXJ0aWNpcGFudAogIAogIHJldHVybihuYW1lMikKfQoKIyBBc3NpZ24gbmFtZSBvZiBvdXRwdXQgZGlyZWN0b3J5CnBsb3RzX2RpciA8LSAicGxvdHMiCgojIENyZWF0ZSBkaXJlY3RvcnkgdG8gaG9sZCB0aGUgb3V0cHV0IHBsb3RzLgppZiAoIWRpci5leGlzdHMocGxvdHNfZGlyKSkgewogIGRpci5jcmVhdGUocGxvdHNfZGlyKQp9CgojIFJlYWQgaW4gZGF0YXNldApkZjIgPC0gZGF0YS5mcmFtZShyZWFkcjo6cmVhZF90c3YoCiAgZmlsZS5wYXRoKCIuLiIsICIuLiIsICJkYXRhIiwgInBidGEtaGlzdG9sb2dpZXMudHN2IikKKSkKCiMgUmVhZCBpbiBrYWxsaXN0byBleHByZXNzaW9uIGRhdGEgCmV4cF9rYWxsaXN0byA8LSBkYXRhLmZyYW1lKHJlYWRyOjpyZWFkX3JkcygKICBmaWxlLnBhdGgoIi4uIiwgIi4uIiwgImRhdGEiLCAicGJ0YS1nZW5lLWV4cHJlc3Npb24ta2FsbGlzdG8ucmRzIikKKSkKCiMgUmVhZCBpbiBSU0VNIGV4cHJlc3Npb24gZGF0YSAKZXhwX3JzZW0gPC0gZGF0YS5mcmFtZShyZWFkcjo6cmVhZF9yZHMoCiAgZmlsZS5wYXRoKCIuLiIsICIuLiIsICJkYXRhIiwgInBidGEtZ2VuZS1leHByZXNzaW9uLXJzZW0uZnBrbS5yZHMiKQopKQpgYGAKCiMgUHJlcCBEYXRhCk1ha2UgdGhlIGV4cHJlc3Npb24gZGF0YS5mcmFtZSBhbGwgbnVtZXJpYyBieSB0cmFuc2Zvcm1pbmcgdGhlIG5vbi1udW1lcmljIGBnZW5lX2lkYCBjb2x1bW4gaW50byByb3duYW1lcy4gClRyYW5zcG9zZSB0aGUgZGF0YSBhbmQgc2F2ZSB0aGUgcm93bmFtZXMgb2YgdGhlIHRyYW5zcG9zZWQgZGF0YS5mcmFtZXMgZm9yIGFsaWdubWVudCB0byB0aGUgbWV0YWRhdGEuIAoKYGBge3J9CiMgVHJhbnNmb3JtIHRoZSBub24tbnVtZXJpYyAiZ2VuZV9pZCIgY29sdW1uIGludG8gcm93bmFtZXMgCmV4cF9rYWxsaXN0byA8LSBleHBfa2FsbGlzdG9bLCAtMV0gJT4lCiAgZHBseXI6OmZpbHRlcighZHVwbGljYXRlZChnZW5lX2lkKSkgJT4lCiAgdGliYmxlOjpjb2x1bW5fdG9fcm93bmFtZXMoImdlbmVfaWQiKQoKZXhwX3JzZW0gPC0gZXhwX3JzZW0gJT4lCiAgdGliYmxlOjpjb2x1bW5fdG9fcm93bmFtZXMoImdlbmVfaWQiKSAlPiUKICBuYS5vbWl0KCkKCiMgVHJhbnNwb3NlIHRoZSBkYXRhIAp0cmFuc3Bvc2VkX3JzZW1fZGF0YSA8LSB0KGV4cF9yc2VtKQoKdHJhbnNwb3NlZF9rYWxsaXN0b19kYXRhIDwtIHQoZXhwX2thbGxpc3RvKQoKIyBTYXZlIHJvd25hbWVzIGFzIGEgdmVjdG9yCnJzZW1fSUQgPC0gcm93bmFtZXModHJhbnNwb3NlZF9yc2VtX2RhdGEpCmthbGxpc3RvX0lEIDwtIHJvd25hbWVzKHRyYW5zcG9zZWRfa2FsbGlzdG9fZGF0YSkKYGBgCgojIFJTRU0gCgojIyBSdW4gUENBCgpgYGB7cn0KIyBSdW4gUENBIG9uIFJTRU0gCnJzZW1fcGNhIDwtIHByY29tcCh0cmFuc3Bvc2VkX3JzZW1fZGF0YSkKIyBNYWtlIGEgZGF0YS5mcmFtZSB3aXRoIFBDQSBzY29yZXMKcnNlbV9wY2FfZGF0YSA8LSBkYXRhLmZyYW1lKHJzZW1fcGNhJHhbLCAxOjJdKQojIFJ1biB0aGUgcmVkdWN0aW9uX2ZuIHdoaWNoIGFsaWducyBtZXRhZGF0YSBhbmQgcHJlcGFyZXMgZGF0YS5mcmFtZSBmb3IgZ2dwbG90CnJzZW1fcGNhX2RhdGEgPC0gcmVkdWN0aW9uX2ZuKHJzZW1fcGNhX2RhdGEsIHJzZW1fSUQpCiMgUGxvdCB0aGUgUENBIHNjb3JlcyBhbmQgY29sb3IgYnkgY2FuY2VyIHR5cGUgCnJzZW1fcGNhX3Bsb3QgPC0gZ2dwbG90Mjo6Z2dwbG90KHJzZW1fcGNhX2RhdGEsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGdncGxvdDI6OmFlcyh4ID0gcnNlbV9wY2FfZGF0YVssIDFdLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgeSA9IHJzZW1fcGNhX2RhdGFbLCAyXSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNvbG9yID0gdHlwZSkpICsKICBnZ3Bsb3QyOjpnZW9tX3BvaW50KCkgKwogIGdncGxvdDI6OnRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIikKCiMgUGxvdCB0aGUgUENBIHNjb3JlcyBhbmQgY29sb3IgYnkgbWV0aG9kCnJzZW1fcGNhX3Bsb3RfbWV0aG9kIDwtIGdncGxvdDI6OmdncGxvdChyc2VtX3BjYV9kYXRhLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBnZ3Bsb3QyOjphZXMoeCA9IHJzZW1fcGNhX2RhdGFbLCAxXSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHkgPSByc2VtX3BjYV9kYXRhWywgMl0sCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjb2xvciA9IG1ldGhvZCkpICsKICBnZ3Bsb3QyOjpnZW9tX3BvaW50KCkgKwogIGdncGxvdDI6OnRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIikKYGBgCgojIyBQQ0EgQmF0Y2ggRWZmZWN0cyBDb3JyZWN0ZWQKCmBgYHtyfQojIFJ1biB0aGUgY29ycmVjdF9iYXRjaF9lZmZlY3RzIGZ1bmN0aW9uIHdoaWNoIGNvcnJlY3RzIGZvciBiYXRjaCBlZmZlY3RzIGFuZCBwcmVwYXJlcyBkYXRhLmZyYW1lIGZvciBnZ3Bsb3QKcnNlbV9wY2FfZGF0YSA8LSBjb3JyZWN0X2JhdGNoX2VmZmVjdHMocnNlbV9wY2FfZGF0YSkKIyBQbG90IHRoZSBQQ0Egc2NvcmVzIGFuZCBjb2xvciBieSBjYW5jZXIgdHlwZSAKcnNlbV9wY2FfcGxvdF8yIDwtIGdncGxvdDI6OmdncGxvdChyc2VtX3BjYV9kYXRhLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBnZ3Bsb3QyOjphZXMoeCA9IHJzZW1fcGNhX2RhdGFbLCAxXSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHkgPSByc2VtX3BjYV9kYXRhWywgMl0sCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjb2xvciA9IHR5cGUpKSArCiAgZ2dwbG90Mjo6Z2VvbV9wb2ludCgpICsKICBnZ3Bsb3QyOjp0aGVtZShsZWdlbmQucG9zaXRpb24gPSAibm9uZSIpCgojIFBsb3QgdGhlIFBDQSBzY29yZXMgYW5kIGNvbG9yIGJ5IG1ldGhvZApyc2VtX3BjYV9wbG90X21ldGhvZF8yIDwtIGdncGxvdDI6OmdncGxvdChyc2VtX3BjYV9kYXRhLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBnZ3Bsb3QyOjphZXMoeCA9IHJzZW1fcGNhX2RhdGFbLCAxXSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHkgPSByc2VtX3BjYV9kYXRhWywgMl0sCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjb2xvciA9IG1ldGhvZCkpICsKICBnZ3Bsb3QyOjpnZW9tX3BvaW50KCkgKwogIGdncGxvdDI6OnRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIikKYGBgCgojIyBSdW4gdC1TTkUKCmBgYHtyfQojIFJ1biB0LVNORSBvbiBSU0VNCnJzZW1fdHNuZSA8LSBSdHNuZTo6UnRzbmUodHJhbnNwb3NlZF9yc2VtX2RhdGEpCiMgTWFrZSBhIGRhdGEuZnJhbWUgd2l0aCB0LVNORSBzY29yZXMKcnNlbV90c25lX2RhdGEgPC0gZGF0YS5mcmFtZShyc2VtX3RzbmUkWSkKIyBSdW4gdGhlIHJlZHVjdGlvbl9mbiB3aGljaCBhbGlnbnMgbWV0YWRhdGEgYW5kIHByZXBhcmVzIGRhdGEuZnJhbWUgZm9yIGdncGxvdApyc2VtX3RzbmVfZGF0YSA8LSByZWR1Y3Rpb25fZm4ocnNlbV90c25lX2RhdGEsIHJzZW1fSUQpCiMgUGxvdCB0aGUgdC1TTkUgc2NvcmVzIGFuZCBjb2xvciBieSBjYW5jZXIgdHlwZSAKcnNlbV90c25lX3Bsb3QgPC0gZ2dwbG90Mjo6Z2dwbG90KHJzZW1fdHNuZV9kYXRhLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZ2dwbG90Mjo6YWVzKHggPSByc2VtX3RzbmVfZGF0YVssIDFdLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHkgPSByc2VtX3RzbmVfZGF0YVssIDJdLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNvbG9yID0gdHlwZSkpICsKICBnZ3Bsb3QyOjpnZW9tX3BvaW50KCkgKwogIGdncGxvdDI6OnRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIikKCiMgUGxvdCB0aGUgdC1TTkUgc2NvcmVzIGFuZCBjb2xvciBieSBtZXRob2QgCnJzZW1fdHNuZV9wbG90X21ldGhvZCA8LSBnZ3Bsb3QyOjpnZ3Bsb3QocnNlbV90c25lX2RhdGEsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBnZ3Bsb3QyOjphZXMoeCA9IHJzZW1fdHNuZV9kYXRhWywgMV0sCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgeSA9IHJzZW1fdHNuZV9kYXRhWywgMl0sCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY29sb3IgPSBtZXRob2QpKSArCiAgZ2dwbG90Mjo6Z2VvbV9wb2ludCgpICsKICBnZ3Bsb3QyOjp0aGVtZShsZWdlbmQucG9zaXRpb24gPSAibm9uZSIpCmBgYAojIyB0LVNORSBCYXRjaCBFZmZlY3RzIENvcnJlY3RlZAoKYGBge3J9CiMgUnVuIHRoZSBjb3JyZWN0X2JhdGNoX2VmZmVjdHMgZnVuY3Rpb24gd2hpY2ggY29ycmVjdHMgZm9yIGJhdGNoIGVmZmVjdHMgYW5kIHByZXBhcmVzIGRhdGEuZnJhbWUgZm9yIGdncGxvdApyc2VtX3RzbmVfZGF0YSA8LSBjb3JyZWN0X2JhdGNoX2VmZmVjdHMocnNlbV90c25lX2RhdGEpCiMgUGxvdCB0aGUgUENBIHNjb3JlcyBhbmQgY29sb3IgYnkgY2FuY2VyIHR5cGUgCnJzZW1fdHNuZV9wbG90XzIgPC0gZ2dwbG90Mjo6Z2dwbG90KHJzZW1fdHNuZV9kYXRhLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBnZ3Bsb3QyOjphZXMoeCA9IHJzZW1fdHNuZV9kYXRhWywgMV0sCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB5ID0gcnNlbV90c25lX2RhdGFbLCAyXSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNvbG9yID0gdHlwZSkpICsKICBnZ3Bsb3QyOjpnZW9tX3BvaW50KCkgKwogIGdncGxvdDI6OnRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIikKCiMgUGxvdCB0aGUgdC1TTkUgc2NvcmVzIGFuZCBjb2xvciBieSBtZXRob2QKcnNlbV90c25lX3Bsb3RfbWV0aG9kXzIgPC0gZ2dwbG90Mjo6Z2dwbG90KHJzZW1fdHNuZV9kYXRhLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBnZ3Bsb3QyOjphZXMoeCA9IHJzZW1fdHNuZV9kYXRhWywgMV0sCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB5ID0gcnNlbV90c25lX2RhdGFbLCAyXSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNvbG9yID0gbWV0aG9kKSkgKwogIGdncGxvdDI6Omdlb21fcG9pbnQoKSArCiAgZ2dwbG90Mjo6dGhlbWUobGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiKQpgYGAKCiMjIFJ1biBVTUFQCgpgYGB7cn0KIyBSdW4gVU1BUCBvbiBSU0VNCnJzZW1fdW1hcCA8LSB1bWFwOjp1bWFwKHRyYW5zcG9zZWRfcnNlbV9kYXRhKQojIE1ha2UgYSBkYXRhLmZyYW1lIHdpdGggdW1hcCBzY29yZXMKcnNlbV91bWFwX2RhdGEgPC0gZGF0YS5mcmFtZShyc2VtX3VtYXAkbGF5b3V0KQojIFJ1biB0aGUgcmVkdWN0aW9uX2ZuIHdoaWNoIGFsaWducyBtZXRhZGF0YSBhbmQgcHJlcGFyZXMgZGF0YS5mcmFtZSBmb3IgZ2dwbG90CnJzZW1fdW1hcF9kYXRhIDwtIHJlZHVjdGlvbl9mbihyc2VtX3VtYXBfZGF0YSwgcnNlbV9JRCkKCiMgUGxvdCB0aGUgdW1hcCBzY29yZXMgYW5kIGNvbG9yIGJ5IGNhbmNlciB0eXBlIApyc2VtX3VtYXBfcGxvdCA8LSBnZ3Bsb3QyOjpnZ3Bsb3QocnNlbV91bWFwX2RhdGEsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBnZ3Bsb3QyOjphZXMoeCA9IHJzZW1fdW1hcF9kYXRhWywgMV0sCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgeSA9IHJzZW1fdW1hcF9kYXRhWywgMl0sCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY29sb3IgPSB0eXBlKSkgKwogIGdncGxvdDI6Omdlb21fcG9pbnQoKSArCiAgZ2dwbG90Mjo6dGhlbWUobGVnZW5kLnBvc2l0aW9uID0gImJvdHRvbSIsIGxlZ2VuZC50ZXh0ID0gZ2dwbG90Mjo6ZWxlbWVudF90ZXh0KHNpemUgPSA0KSkKCiMgRXh0cmFjdCB0aGUgcGxvdCBsZWdlbmQKbGVnZW5kIDwtIGxlbW9uOjpnX2xlZ2VuZChyc2VtX3VtYXBfcGxvdCkKCiMgUGxvdCB0aGUgdW1hcCBzY29yZXMgYW5kIGNvbG9yIGJ5IG1ldGhvZCAKcnNlbV91bWFwX3Bsb3RfbWV0aG9kIDwtIGdncGxvdDI6OmdncGxvdChyc2VtX3VtYXBfZGF0YSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGdncGxvdDI6OmFlcyh4ID0gcnNlbV91bWFwX2RhdGFbLCAxXSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB5ID0gcnNlbV91bWFwX2RhdGFbLCAyXSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjb2xvciA9IG1ldGhvZCkpICsKICBnZ3Bsb3QyOjpnZW9tX3BvaW50KCkgKwogIGdncGxvdDI6OnRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJib3R0b20iLCBsZWdlbmQudGV4dCA9IGdncGxvdDI6OmVsZW1lbnRfdGV4dChzaXplID0gMTQpKQoKI0V4dHJhY3QgdGhlIHBsb3QgbGVnZW5kCmxlZ2VuZDIgPC0gbGVtb246OmdfbGVnZW5kKHJzZW1fdW1hcF9wbG90X21ldGhvZCkKYGBgCiMjIFVNQVAgQmF0Y2ggRWZmZWN0cyBDb3JyZWN0ZWQKCmBgYHtyfQojIFJ1biB0aGUgY29ycmVjdF9iYXRjaF9lZmZlY3RzIGZ1bmN0aW9uIHdoaWNoIGNvcnJlY3RzIGZvciBiYXRjaCBlZmZlY3RzIGFuZCBwcmVwYXJlcyBkYXRhLmZyYW1lIGZvciBnZ3Bsb3QKcnNlbV91bWFwX2RhdGEgPC0gY29ycmVjdF9iYXRjaF9lZmZlY3RzKHJzZW1fdW1hcF9kYXRhKQojIFBsb3QgdGhlIFBDQSBzY29yZXMgYW5kIGNvbG9yIGJ5IGNhbmNlciB0eXBlIApyc2VtX3VtYXBfcGxvdF8yIDwtIGdncGxvdDI6OmdncGxvdChyc2VtX3VtYXBfZGF0YSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZ2dwbG90Mjo6YWVzKHggPSByc2VtX3VtYXBfZGF0YVssIDFdLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgeSA9IHJzZW1fdW1hcF9kYXRhWywgMl0sCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjb2xvciA9IHR5cGUpKSArCiAgZ2dwbG90Mjo6Z2VvbV9wb2ludCgpICsKICBnZ3Bsb3QyOjp0aGVtZShsZWdlbmQucG9zaXRpb24gPSAiYm90dG9tIiwgbGVnZW5kLnRleHQgPSBnZ3Bsb3QyOjplbGVtZW50X3RleHQoc2l6ZSA9IDQpKQoKIyBFeHRyYWN0IHRoZSBwbG90IGxlZ2VuZApsZWdlbmRfYmF0Y2ggPC0gbGVtb246OmdfbGVnZW5kKHJzZW1fdW1hcF9wbG90XzIpCgojIFBsb3QgdGhlIHVtYXAgc2NvcmVzIGFuZCBjb2xvciBieSBtZXRob2QKcnNlbV91bWFwX3Bsb3RfbWV0aG9kXzIgPC0gZ2dwbG90Mjo6Z2dwbG90KHJzZW1fdW1hcF9kYXRhLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBnZ3Bsb3QyOjphZXMoeCA9IHJzZW1fdW1hcF9kYXRhWywgMV0sCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB5ID0gcnNlbV91bWFwX2RhdGFbLCAyXSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNvbG9yID0gbWV0aG9kKSkgKwogIGdncGxvdDI6Omdlb21fcG9pbnQoKSArCiAgZ2dwbG90Mjo6dGhlbWUobGVnZW5kLnBvc2l0aW9uID0gImJvdHRvbSIsIGxlZ2VuZC50ZXh0ID0gZ2dwbG90Mjo6ZWxlbWVudF90ZXh0KHNpemUgPSAxNCkpCgojRXh0cmFjdCB0aGUgcGxvdCBsZWdlbmQKbGVnZW5kMl9iYXRjaCA8LSBsZW1vbjo6Z19sZWdlbmQocnNlbV91bWFwX3Bsb3RfbWV0aG9kXzIpCmBgYAoKIyMgUlNFTSBQbG90cyBHcmlkIApUaGUgZ3JpZCBvZiBwbG90cyBiZWxvdyBzaG93cyB0aGUgZGltZW5zaW9uIHJlZHVjdGlvbnMgc2NvcmVzIGZvciB0aGUgUlNFTSBkYXRhLCBjb2xvcmVkIGJ5IGV4cGVyaW1lbnRhbCBzZXF1ZW5jaW5nIHN0cmF0ZWd5IGFuZCB0dW1vciB0eXBlLCByZXNwZWN0aXZlbHkuICAKVGhlIHRvcCBzZXQgb2YgcGxvdHMgcmVwcmVzZW50IHRoZSBkYXRhIGJlZm9yZSBiYXRjaCBlZmZlY3RzIHdlcmUgY29ycmVjdGVkLCB3aGlsZSB0aGUgYm90dG9tIHNldCBvZiBwbG90cyByZXByZXNlbnQgdGhlIGRhdGEgYWZ0ZXIgYmF0Y2ggZWZmZWN0cyB3ZXJlIGNvcnJlY3RlZC4KV2hlbiBjb21wYXJpbmcgdGhlIHBsb3RzLCB0aGUgcHJlc2VuY2Ugb2YgYmF0Y2ggZWZmZWN0cyBkb2VzIG5vdCBhcHBlYXIgdG8gYmUgdmVyeSBzaWduaWZpY2FudC4gCgpgYGB7ciwgZmlnLndpZHRoID0gMTcsIGZpZy5oZWlnaHQgPSAxNH0KIyBQbG90IGdyaWQgd2l0aCBSU0VNIGRhdGEgY29sb3JlZCBieSBtZXRob2QgdnMgdHVtb3IgdHlwZQptZXRhX2dyaWRfcnNlbV9tZXRob2QgPC0gZ3JpZEV4dHJhOjpncmlkLmFycmFuZ2UocnNlbV9wY2FfcGxvdF9tZXRob2QsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICByc2VtX3RzbmVfcGxvdF9tZXRob2QsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICByc2VtX3VtYXBfcGxvdF9tZXRob2QgKyAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZ2dwbG90Mjo6dGhlbWUobGVnZW5kLnBvc2l0aW9uID0gJ2hpZGRlbicpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbnJvdyA9IDIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBsZWdlbmQyLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgdG9wID0gIkV4cGVyaW1lbnRhbCBTdHJhdGVneSAoUlNFTSkiKQoKbWV0YV9ncmlkX3JzZW1fbWV0aG9kXzIgPC0gZ3JpZEV4dHJhOjpncmlkLmFycmFuZ2UocnNlbV9wY2FfcGxvdF9tZXRob2RfMiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHJzZW1fdHNuZV9wbG90X21ldGhvZF8yLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcnNlbV91bWFwX3Bsb3RfbWV0aG9kXzIgKyAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZ2dwbG90Mjo6dGhlbWUobGVnZW5kLnBvc2l0aW9uID0gJ2hpZGRlbicpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbnJvdyA9IDIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBsZWdlbmQyX2JhdGNoLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgdG9wID0gIkV4cGVyaW1lbnRhbCBTdHJhdGVneSAtIEJhdGNoIEVmZmVjdHMgQ29ycmVjdGVkIChSU0VNKSIpCgptZXRhX2dyaWRfcnNlbV90eXBlIDwtIGdyaWRFeHRyYTo6Z3JpZC5hcnJhbmdlKHJzZW1fcGNhX3Bsb3QsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICByc2VtX3RzbmVfcGxvdCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHJzZW1fdW1hcF9wbG90ICsgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGdncGxvdDI6OnRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICdoaWRkZW4nKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG5yb3cgPSAzLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbGVnZW5kLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgdG9wID0gIkV4cHJlc3NlZCBUdW1vciBUeXBlcyAoUlNFTSkiKQoKbWV0YV9ncmlkX3JzZW1fdHlwZV8yIDwtIGdyaWRFeHRyYTo6Z3JpZC5hcnJhbmdlKHJzZW1fcGNhX3Bsb3RfMiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHJzZW1fdHNuZV9wbG90XzIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICByc2VtX3VtYXBfcGxvdF8yICsgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGdncGxvdDI6OnRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICdoaWRkZW4nKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG5yb3cgPSAzLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbGVnZW5kX2JhdGNoLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgdG9wID0gIkV4cHJlc3NlZCBUdW1vciBUeXBlcyAtIEJhdGNoIEVmZmVjdHMgQ29ycmVjdGVkIChSU0VNKSIpCiMgU2F2ZSBncmlkCmdncGxvdDI6Omdnc2F2ZShmaWxlLnBhdGgocGxvdHNfZGlyLCAibWV0YV9ncmlkX3JzZW1fbWV0aG9kLnBkZiIpLCBtZXRhX2dyaWRfcnNlbV9tZXRob2QsIHdpZHRoID0gMTIsIGhlaWdodCA9IDE4KQpnZ3Bsb3QyOjpnZ3NhdmUoZmlsZS5wYXRoKHBsb3RzX2RpciwgIm1ldGFfZ3JpZF9yc2VtX3R5cGUucGRmIiksIG1ldGFfZ3JpZF9yc2VtX3R5cGUsIHdpZHRoID0gMTIsIGhlaWdodCA9IDE4KQpnZ3Bsb3QyOjpnZ3NhdmUoZmlsZS5wYXRoKHBsb3RzX2RpciwgIm5vX2JhdGNoX2dyaWRfcnNlbV9tZXRob2QucGRmIiksIG1ldGFfZ3JpZF9yc2VtX21ldGhvZF8yLCB3aWR0aCA9IDEyLCBoZWlnaHQgPSAxOCkKZ2dwbG90Mjo6Z2dzYXZlKGZpbGUucGF0aChwbG90c19kaXIsICJub19iYXRjaF9ncmlkX3JzZW1fdHlwZS5wZGYiKSwgbWV0YV9ncmlkX3JzZW1fdHlwZV8yLCB3aWR0aCA9IDEyLCBoZWlnaHQgPSAxOCkKYGBgCgoKIyBLYWxsaXN0bwoKIyMgUnVuIFBDQQoKYGBge3J9CiMgUnVuIFBDQSBvbiBrYWxsaXN0bwprYWxsaXN0b19wY2EgPC0gcHJjb21wKHRyYW5zcG9zZWRfa2FsbGlzdG9fZGF0YSkKIyBNYWtlIGEgZGF0YS5mcmFtZSB3aXRoIFBDQSBzY29yZXMKa2FsbGlzdG9fcGNhX2RhdGEgPC0gZGF0YS5mcmFtZShrYWxsaXN0b19wY2EkeFssIDE6Ml0pCiMgUnVuIHRoZSByZWR1Y3Rpb25fZm4gd2hpY2ggYWxpZ25zIG1ldGFkYXRhIGFuZCBwcmVwYXJlcyBkYXRhLmZyYW1lIGZvciBnZ3Bsb3QKa2FsbGlzdG9fcGNhX2RhdGEgPC0gcmVkdWN0aW9uX2ZuKGthbGxpc3RvX3BjYV9kYXRhLCBrYWxsaXN0b19JRCkKIyBQbG90IHRoZSBQQ0Egc2NvcmVzIGFuZCBjb2xvciBieSBjYW5jZXIgdHlwZSAKa2FsbGlzdG9fcGNhX3Bsb3QgPC0gZ2dwbG90Mjo6Z2dwbG90KGthbGxpc3RvX3BjYV9kYXRhLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZ2dwbG90Mjo6YWVzKHggPSBrYWxsaXN0b19wY2FfZGF0YVssIDFdLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHkgPSBrYWxsaXN0b19wY2FfZGF0YVssIDJdLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNvbG9yID0gdHlwZSkpICsKICBnZ3Bsb3QyOjpnZW9tX3BvaW50KCkgKwogIGdncGxvdDI6OnRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIikKCiMgUGxvdCB0aGUgUENBIHNjb3JlcyBhbmQgY29sb3IgYnkgbWV0aG9kCmthbGxpc3RvX3BjYV9wbG90X21ldGhvZCA8LSBnZ3Bsb3QyOjpnZ3Bsb3Qoa2FsbGlzdG9fcGNhX2RhdGEsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBnZ3Bsb3QyOjphZXMoeCA9IGthbGxpc3RvX3BjYV9kYXRhWywgMV0sCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgeSA9IGthbGxpc3RvX3BjYV9kYXRhWywgMl0sCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY29sb3IgPSBtZXRob2QpKSArCiAgZ2dwbG90Mjo6Z2VvbV9wb2ludCgpICsKICBnZ3Bsb3QyOjp0aGVtZShsZWdlbmQucG9zaXRpb24gPSAibm9uZSIpCmBgYAoKIyMgUENBIEJhdGNoIEVmZmVjdHMgQ29ycmVjdGVkCgpgYGB7cn0KIyBSdW4gdGhlIGNvcnJlY3RfYmF0Y2hfZWZmZWN0cyBmdW5jdGlvbiB3aGljaCBjb3JyZWN0cyBmb3IgYmF0Y2ggZWZmZWN0cyBhbmQgcHJlcGFyZXMgZGF0YS5mcmFtZSBmb3IgZ2dwbG90CmthbGxpc3RvX3BjYV9kYXRhIDwtIGNvcnJlY3RfYmF0Y2hfZWZmZWN0cyhrYWxsaXN0b19wY2FfZGF0YSkKIyBQbG90IHRoZSBQQ0Egc2NvcmVzIGFuZCBjb2xvciBieSBjYW5jZXIgdHlwZSAKa2FsbGlzdG9fcGNhX3Bsb3RfMiA8LSBnZ3Bsb3QyOjpnZ3Bsb3Qoa2FsbGlzdG9fcGNhX2RhdGEsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGdncGxvdDI6OmFlcyh4ID0ga2FsbGlzdG9fcGNhX2RhdGFbLCAxXSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHkgPSBrYWxsaXN0b19wY2FfZGF0YVssIDJdLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY29sb3IgPSB0eXBlKSkgKwogIGdncGxvdDI6Omdlb21fcG9pbnQoKSArCiAgZ2dwbG90Mjo6dGhlbWUobGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiKQoKIyBQbG90IHRoZSBQQ0Egc2NvcmVzIGFuZCBjb2xvciBieSBtZXRob2QKa2FsbGlzdG9fcGNhX3Bsb3RfbWV0aG9kXzIgPC0gZ2dwbG90Mjo6Z2dwbG90KGthbGxpc3RvX3BjYV9kYXRhLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBnZ3Bsb3QyOjphZXMoeCA9IGthbGxpc3RvX3BjYV9kYXRhWywgMV0sCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB5ID0ga2FsbGlzdG9fcGNhX2RhdGFbLCAyXSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNvbG9yID0gbWV0aG9kKSkgKwogIGdncGxvdDI6Omdlb21fcG9pbnQoKSArCiAgZ2dwbG90Mjo6dGhlbWUobGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiKQpgYGAKCiMjIFJ1biB0LVNORSAKCmBgYHtyfQojIFJ1biB0LVNORSBvbiBrYWxsaXN0bwprYWxsaXN0b190c25lIDwtIFJ0c25lOjpSdHNuZSh0cmFuc3Bvc2VkX2thbGxpc3RvX2RhdGEpCiMgTWFrZSBhIGRhdGEuZnJhbWUgd2l0aCB0LVNORSBzY29yZXMKa2FsbGlzdG9fdHNuZV9kYXRhIDwtIGRhdGEuZnJhbWUoa2FsbGlzdG9fdHNuZSRZKQojIFJ1biB0aGUgcmVkdWN0aW9uX2ZuIHdoaWNoIGFsaWducyBtZXRhZGF0YSBhbmQgcHJlcGFyZXMgZGF0YS5mcmFtZSBmb3IgZ2dwbG90CmthbGxpc3RvX3RzbmVfZGF0YSA8LSByZWR1Y3Rpb25fZm4oa2FsbGlzdG9fdHNuZV9kYXRhLCBrYWxsaXN0b19JRCkKIyBQbG90IHRoZSB0LVNORSBzY29yZXMgYW5kIGNvbG9yIGJ5IGNhbmNlciB0eXBlIAprYWxsaXN0b190c25lX3Bsb3QgPC0gZ2dwbG90Mjo6Z2dwbG90KGthbGxpc3RvX3RzbmVfZGF0YSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBnZ3Bsb3QyOjphZXMoeCA9IGthbGxpc3RvX3RzbmVfZGF0YVssIDFdLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB5ID0ga2FsbGlzdG9fdHNuZV9kYXRhWywgMl0sCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNvbG9yID0gdHlwZSkpICsKICBnZ3Bsb3QyOjpnZW9tX3BvaW50KCkgKwogIGdncGxvdDI6OnRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIikKCiMgUGxvdCB0aGUgdC1TTkUgc2NvcmVzIGFuZCBjb2xvciBieSBtZXRob2QgCmthbGxpc3RvX3RzbmVfcGxvdF9tZXRob2QgPC0gZ2dwbG90Mjo6Z2dwbG90KGthbGxpc3RvX3RzbmVfZGF0YSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBnZ3Bsb3QyOjphZXMoeCA9IGthbGxpc3RvX3RzbmVfZGF0YVssIDFdLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB5ID0ga2FsbGlzdG9fdHNuZV9kYXRhWywgMl0sCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNvbG9yID0gbWV0aG9kKSkgKwogIGdncGxvdDI6Omdlb21fcG9pbnQoKSArCiAgZ2dwbG90Mjo6dGhlbWUobGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiKQpgYGAKCiMjIHQtU05FIEJhdGNoIEVmZmVjdHMgQ29ycmVjdGVkCgpgYGB7cn0KIyBSdW4gdGhlIGNvcnJlY3RfYmF0Y2hfZWZmZWN0cyBmdW5jdGlvbiB3aGljaCBjb3JyZWN0cyBmb3IgYmF0Y2ggZWZmZWN0cyBhbmQgcHJlcGFyZXMgZGF0YS5mcmFtZSBmb3IgZ2dwbG90CmthbGxpc3RvX3RzbmVfZGF0YSA8LSBjb3JyZWN0X2JhdGNoX2VmZmVjdHMoa2FsbGlzdG9fdHNuZV9kYXRhKQojIFBsb3QgdGhlIFBDQSBzY29yZXMgYW5kIGNvbG9yIGJ5IGNhbmNlciB0eXBlIAprYWxsaXN0b190c25lX3Bsb3RfMiA8LSBnZ3Bsb3QyOjpnZ3Bsb3Qoa2FsbGlzdG9fdHNuZV9kYXRhLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBnZ3Bsb3QyOjphZXMoeCA9IGthbGxpc3RvX3RzbmVfZGF0YVssIDFdLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgeSA9IGthbGxpc3RvX3RzbmVfZGF0YVssIDJdLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY29sb3IgPSB0eXBlKSkgKwogIGdncGxvdDI6Omdlb21fcG9pbnQoKSArCiAgZ2dwbG90Mjo6dGhlbWUobGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiKQoKIyBQbG90IHRoZSB0LVNORSBzY29yZXMgYW5kIGNvbG9yIGJ5IG1ldGhvZAprYWxsaXN0b190c25lX3Bsb3RfbWV0aG9kXzIgPC0gZ2dwbG90Mjo6Z2dwbG90KGthbGxpc3RvX3RzbmVfZGF0YSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZ2dwbG90Mjo6YWVzKHggPSBrYWxsaXN0b190c25lX2RhdGFbLCAxXSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHkgPSBrYWxsaXN0b190c25lX2RhdGFbLCAyXSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNvbG9yID0gbWV0aG9kKSkgKwogIGdncGxvdDI6Omdlb21fcG9pbnQoKSArCiAgZ2dwbG90Mjo6dGhlbWUobGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiKQpgYGAKCiMjIFJ1biBVTUFQIAoKYGBge3J9CiMgUnVuIFVNQVAgb24ga2FsbGlzdG8Ka2FsbGlzdG9fdW1hcCA8LSB1bWFwOjp1bWFwKHRyYW5zcG9zZWRfa2FsbGlzdG9fZGF0YSkKIyBNYWtlIGEgZGF0YS5mcmFtZSB3aXRoIHVtYXAgc2NvcmVzCmthbGxpc3RvX3VtYXBfZGF0YSA8LSBkYXRhLmZyYW1lKGthbGxpc3RvX3VtYXAkbGF5b3V0KQojIFJ1biB0aGUgcmVkdWN0aW9uX2ZuIHdoaWNoIGFsaWducyBtZXRhZGF0YSBhbmQgcHJlcGFyZXMgZGF0YS5mcmFtZSBmb3IgZ2dwbG90CmthbGxpc3RvX3VtYXBfZGF0YSA8LSByZWR1Y3Rpb25fZm4oa2FsbGlzdG9fdW1hcF9kYXRhLCBrYWxsaXN0b19JRCkKIyBQbG90IHRoZSB1bWFwIHNjb3JlcyBhbmQgY29sb3IgYnkgY2FuY2VyIHR5cGUgCmthbGxpc3RvX3VtYXBfcGxvdCA8LSBnZ3Bsb3QyOjpnZ3Bsb3Qoa2FsbGlzdG9fdW1hcF9kYXRhLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGdncGxvdDI6OmFlcyh4ID0ga2FsbGlzdG9fdW1hcF9kYXRhWywgMV0sCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHkgPSBrYWxsaXN0b191bWFwX2RhdGFbLCAyXSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY29sb3IgPSB0eXBlKSkgKwogIGdncGxvdDI6Omdlb21fcG9pbnQoKSArCiAgZ2dwbG90Mjo6dGhlbWUobGVnZW5kLnBvc2l0aW9uID0gImJvdHRvbSIsIGxlZ2VuZC50ZXh0ID0gZ2dwbG90Mjo6ZWxlbWVudF90ZXh0KHNpemUgPSA0KSkKCiNFeHRyYWN0IHRoZSBwbG90IGxlZ2VuZApsZWdlbmQgPC0gbGVtb246OmdfbGVnZW5kKGthbGxpc3RvX3VtYXBfcGxvdCkKCiMgUGxvdCB0aGUgdW1hcCBzY29yZXMgYW5kIGNvbG9yIGJ5IG1ldGhvZAprYWxsaXN0b191bWFwX3Bsb3RfbWV0aG9kIDwtIGdncGxvdDI6OmdncGxvdChrYWxsaXN0b191bWFwX2RhdGEsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZ2dwbG90Mjo6YWVzKHggPSBrYWxsaXN0b191bWFwX2RhdGFbLCAxXSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgeSA9IGthbGxpc3RvX3VtYXBfZGF0YVssIDJdLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjb2xvciA9IG1ldGhvZCkpICsKICBnZ3Bsb3QyOjpnZW9tX3BvaW50KCkgKwogIGdncGxvdDI6OnRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJib3R0b20iLCBsZWdlbmQudGV4dCA9IGdncGxvdDI6OmVsZW1lbnRfdGV4dChzaXplID0gMTQpKQoKIyBFeHRyYWN0IHRoZSBwbG90IGxlZ2VuZCAKbGVnZW5kMiA8LSBsZW1vbjo6Z19sZWdlbmQoa2FsbGlzdG9fdW1hcF9wbG90X21ldGhvZCkKYGBgCgojIyBVTUFQIEJhdGNoIEVmZmVjdHMgQ29ycmVjdGVkCgpgYGB7cn0KIyBSdW4gdGhlIGNvcnJlY3RfYmF0Y2hfZWZmZWN0cyBmdW5jdGlvbiB3aGljaCBjb3JyZWN0cyBmb3IgYmF0Y2ggZWZmZWN0cyBhbmQgcHJlcGFyZXMgZGF0YS5mcmFtZSBmb3IgZ2dwbG90CmthbGxpc3RvX3VtYXBfZGF0YSA8LSBjb3JyZWN0X2JhdGNoX2VmZmVjdHMoa2FsbGlzdG9fdW1hcF9kYXRhKQojIFBsb3QgdGhlIFBDQSBzY29yZXMgYW5kIGNvbG9yIGJ5IGNhbmNlciB0eXBlIAprYWxsaXN0b191bWFwX3Bsb3RfMiA8LSBnZ3Bsb3QyOjpnZ3Bsb3Qoa2FsbGlzdG9fdW1hcF9kYXRhLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBnZ3Bsb3QyOjphZXMoeCA9IGthbGxpc3RvX3VtYXBfZGF0YVssIDFdLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgeSA9IGthbGxpc3RvX3VtYXBfZGF0YVssIDJdLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY29sb3IgPSB0eXBlKSkgKwogIGdncGxvdDI6Omdlb21fcG9pbnQoKSArCiAgZ2dwbG90Mjo6dGhlbWUobGVnZW5kLnBvc2l0aW9uID0gImJvdHRvbSIsIGxlZ2VuZC50ZXh0ID0gZ2dwbG90Mjo6ZWxlbWVudF90ZXh0KHNpemUgPSA0KSkKCiMgRXh0cmFjdCB0aGUgcGxvdCBsZWdlbmQKbGVnZW5kX2JhdGNoIDwtIGxlbW9uOjpnX2xlZ2VuZChrYWxsaXN0b191bWFwX3Bsb3RfMikKCiMgUGxvdCB0aGUgdW1hcCBzY29yZXMgYW5kIGNvbG9yIGJ5IG1ldGhvZAprYWxsaXN0b191bWFwX3Bsb3RfbWV0aG9kXzIgPC0gZ2dwbG90Mjo6Z2dwbG90KGthbGxpc3RvX3VtYXBfZGF0YSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZ2dwbG90Mjo6YWVzKHggPSBrYWxsaXN0b191bWFwX2RhdGFbLCAxXSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHkgPSBrYWxsaXN0b191bWFwX2RhdGFbLCAyXSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNvbG9yID0gbWV0aG9kKSkgKwogIGdncGxvdDI6Omdlb21fcG9pbnQoKSArCiAgZ2dwbG90Mjo6dGhlbWUobGVnZW5kLnBvc2l0aW9uID0gImJvdHRvbSIsIGxlZ2VuZC50ZXh0ID0gZ2dwbG90Mjo6ZWxlbWVudF90ZXh0KHNpemUgPSAxNCkpCgojRXh0cmFjdCB0aGUgcGxvdCBsZWdlbmQKbGVnZW5kMl9iYXRjaCA8LSBsZW1vbjo6Z19sZWdlbmQoa2FsbGlzdG9fdW1hcF9wbG90X21ldGhvZF8yKQpgYGAKCiMjIEthbGxpc3RvIFBsb3RzIEdyaWQKVGhlIGdyaWQgb2YgcGxvdHMgYmVsb3cgc2hvd3MgdGhlIGRpbWVuc2lvbiByZWR1Y3Rpb25zIHNjb3JlcyBmb3IgdGhlIGthbGxpc3RvIGRhdGEsIGNvbG9yZWQgYnkgZXhwZXJpbWVudGFsIHNlcXVlbmNpbmcgc3RyYXRlZ3kgYW5kIHR1bW9yIHR5cGUsIHJlc3BlY3RpdmVseS4gClRoZSB0b3Agc2V0IG9mIHBsb3RzIHJlcHJlc2VudCB0aGUgZGF0YSBiZWZvcmUgYmF0Y2ggZWZmZWN0cyB3ZXJlIGNvcnJlY3RlZCwgd2hpbGUgdGhlIGJvdHRvbSBzZXQgb2YgcGxvdHMgcmVwcmVzZW50IHRoZSBkYXRhIGFmdGVyIGJhdGNoIGVmZmVjdHMgd2VyZSBjb3JyZWN0ZWQuCldoZW4gY29tcGFyaW5nIHRoZSBwbG90cywgdGhlIHByZXNlbmNlIG9mIGJhdGNoIGVmZmVjdHMgZG9lcyBub3QgYXBwZWFyIHRvIGJlIHZlcnkgc2lnbmlmaWNhbnQuIAoKYGBge3IsIGZpZy53aWR0aCA9IDE3LCBmaWcuaGVpZ2h0ID0gMTR9CiMgUGxvdCBncmlkIHdpdGgga2FsbGlzdG8gZGF0YSBjb2xvcmVkIGJ5IG1ldGhvZCB2cyB0dW1vciB0eXBlCm1ldGFfZ3JpZF9rYWxsaXN0b19tZXRob2QgPC0gZ3JpZEV4dHJhOjpncmlkLmFycmFuZ2Uoa2FsbGlzdG9fcGNhX3Bsb3RfbWV0aG9kLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAga2FsbGlzdG9fdHNuZV9wbG90X21ldGhvZCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGthbGxpc3RvX3VtYXBfcGxvdF9tZXRob2QgKyAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZ2dwbG90Mjo6dGhlbWUobGVnZW5kLnBvc2l0aW9uID0gJ2hpZGRlbicpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbGVnZW5kMiwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBucm93ID0gMiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHRvcCA9ICJFeHBlcmltZW50YWwgU3RyYXRlZ3kgKGthbGxpc3RvKSIpCgptZXRhX2dyaWRfa2FsbGlzdG9fbWV0aG9kXzIgPC0gZ3JpZEV4dHJhOjpncmlkLmFycmFuZ2Uoa2FsbGlzdG9fcGNhX3Bsb3RfbWV0aG9kXzIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBrYWxsaXN0b190c25lX3Bsb3RfbWV0aG9kXzIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBrYWxsaXN0b191bWFwX3Bsb3RfbWV0aG9kXzIgKyAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZ2dwbG90Mjo6dGhlbWUobGVnZW5kLnBvc2l0aW9uID0gJ2hpZGRlbicpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbnJvdyA9IDIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBsZWdlbmQyX2JhdGNoLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgdG9wID0gIkV4cGVyaW1lbnRhbCBTdHJhdGVneSAtIEJhdGNoIEVmZmVjdHMgQ29ycmVjdGVkIChrYWxsaXN0bykiKQoKbWV0YV9ncmlkX2thbGxpc3RvX3R5cGUgPC0gZ3JpZEV4dHJhOjpncmlkLmFycmFuZ2Uoa2FsbGlzdG9fcGNhX3Bsb3QsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBrYWxsaXN0b190c25lX3Bsb3QsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBrYWxsaXN0b191bWFwX3Bsb3QgKyAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZ2dwbG90Mjo6dGhlbWUobGVnZW5kLnBvc2l0aW9uID0gJ2hpZGRlbicpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbGVnZW5kLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbnJvdyA9IDMsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB0b3AgPSAiRXhwcmVzc2VkIFR1bW9yIFR5cGVzIChrYWxsaXN0bykiKQoKbWV0YV9ncmlkX2thbGxpc3RvX3R5cGVfMiA8LSBncmlkRXh0cmE6OmdyaWQuYXJyYW5nZShrYWxsaXN0b19wY2FfcGxvdF8yLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAga2FsbGlzdG9fdHNuZV9wbG90XzIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBrYWxsaXN0b191bWFwX3Bsb3RfMiArIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBnZ3Bsb3QyOjp0aGVtZShsZWdlbmQucG9zaXRpb24gPSAnaGlkZGVuJyksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBucm93ID0gMywKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGxlZ2VuZF9iYXRjaCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHRvcCA9ICJFeHByZXNzZWQgVHVtb3IgVHlwZXMgLSBCYXRjaCBFZmZlY3RzIENvcnJlY3RlZCAoa2FsbGlzdG8pIikKIyBTYXZlIGdyaWQKZ2dwbG90Mjo6Z2dzYXZlKGZpbGUucGF0aChwbG90c19kaXIsICJtZXRhX2dyaWRfa2FsbGlzdG9fbWV0aG9kLnBkZiIpLCBtZXRhX2dyaWRfa2FsbGlzdG9fbWV0aG9kLCB3aWR0aCA9IDEyLCBoZWlnaHQgPSAxOSkKZ2dwbG90Mjo6Z2dzYXZlKGZpbGUucGF0aChwbG90c19kaXIsICJtZXRhX2dyaWRfa2FsbGlzdG9fdHlwZS5wZGYiKSwgbWV0YV9ncmlkX2thbGxpc3RvX3R5cGUsIHdpZHRoID0gMTIsIGhlaWdodCA9IDE5KQpnZ3Bsb3QyOjpnZ3NhdmUoZmlsZS5wYXRoKHBsb3RzX2RpciwgIm5vX2JhdGNoX2dyaWRfa2FsbGlzdG9fbWV0aG9kLnBkZiIpLCBtZXRhX2dyaWRfa2FsbGlzdG9fbWV0aG9kXzIsIHdpZHRoID0gMTIsIGhlaWdodCA9IDE4KQpnZ3Bsb3QyOjpnZ3NhdmUoZmlsZS5wYXRoKHBsb3RzX2RpciwgIm5vX2JhdGNoX2dyaWRfa2FsbGlzdG9fdHlwZS5wZGYiKSwgbWV0YV9ncmlkX2thbGxpc3RvX3R5cGVfMiwgd2lkdGggPSAxMiwgaGVpZ2h0ID0gMTgpCmBgYAoKIyBTZXNzaW9uIEluZm8KCmBgYHtyfQpzZXNzaW9uSW5mbygpCmBgYAoK